import sys
import importlib

from django.conf import settings
from django.db.utils import OperationalError

from .models import Setting


def all_forms_fields_types():
    # import form modules
    for form_module in settings.SETTINGS_FORMS:
        try:
            importlib.import_module(form_module)
        except ImportError as e:
            print(f'{e}, check SETTINGS_FORMS list from your settings!')
            sys.exit(1)

    from .core import SettingsWrapper

    forms = SettingsWrapper.forms_class()

    all_fields_types = {}
    for form in forms:
        try:
            all_fields_types.update(form.fields_types)
        except AttributeError:
            pass

    return all_fields_types


class BaseSettingsManager:
    _instance = None

    def __init__(self, all_fields_types=None):

        self.all_fields_types = {} if all_fields_types is None else all_fields_types

        self.all_settings = {}
        self.update_all_settings()

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            return super(cls, cls).__new__(cls)
        raise TypeError(f"Can't create instance directly of {cls!r}")

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls(all_fields_types=all_forms_fields_types())

        return cls._instance

    def update(self, options=None):

        if not isinstance(options, dict):
            raise ValueError(
                "Parameter of 'update' fonction should be a 'dict'.")

        for name, value in options.items():
            self._update_or_create(name, value)

        self.update_all_settings()

    def _update_or_create(self, name, value=None):
        if value is None:
            value = ""

        updated = Setting.objects.filter(
            option_name=name.upper()).update(option_value=value)
        if not updated:
            Setting.objects.create(
                option_name=name.upper(), option_value=value)
            return 1  # 1 means 1 row updated/created
        return updated

    def get_all_settings(self):
        return self.all_settings

    def update_all_settings(self):
        try:
            options = Setting.objects.all()
            # parse as dict
            options = {
                option.option_name: option.option_value for option in options}

            # Do the parsing.
            for name, value in options.items():
                parser_function = self.all_fields_types.get(name, None)
                if parser_function:
                    options[name] = parser_function(value)

            self.all_settings = options
        except Exception as e:
            print(e)
            self.all_settings = {}
        return self.all_settings

    def get(self, name):
        return self.all_settings.get(name, "")

    def delete(self, name):
        return Setting.objects.filter(option_name=name.upper()).delete()


SettingsManager = BaseSettingsManager.get_instance()


# for ease of use.
def all_settings():
    return SettingsManager.get_all_settings()


def get_setting(name):
    value = SettingsManager.get(name)
    if value:
        return value

    return getattr(settings, name, '')
